package clevents;

use Carp qw(cluck);
use cldb;
use Sys::Hostname;
use clreq;
use clreqdxml;
use Socket;
use clutils qw(get_master_node);
use strict;
use Data::Dumper;

sub new {
my $self;
my $class=shift;
my $cluster=shift;

	# Now much in the class really - we attempt to 
	$self={};
	$self->{cluster}=$cluster;
	$self->{sent}=0;
	$self->{lastsent}=0;
	bless($self,$class);
	return $self;
}

#################################################################
# We expect at least the following to be set:			#
# 'event','node'						#
# If the event is app then 'app' must also be specified.	#
# atext - Sends out additional text with the alert too.		#
# Retrun codes:							#
# 0,cnt,errors	- Ok - cnt messages sent, errors is a ref to	#
#			array of warnings/errors.		#
#################################################################

{
my $last_event=undef;
my $last_app=undef;
my $last_node=undef;
my $last_time=time;

sub send_msg {
my $self=shift;
my %args=@_;

	if(!exists($args{daemon}) || !defined($args{daemon})) { $args{daemon}="N/A"; }
	if(!exists($args{node}) || !defined($args{node})) { $args{node}="N/A"; }
	if(!exists($args{app}) || !defined($args{app})) { $args{app}="N/A"; }
	if(!exists($args{net}) || !defined($args{net})) { $args{net}="N/A"; }

	my @warns=();
	return(-1,0,["Missing mandatory 'event' argument."]) if !exists($args{event});
	return(-1,0,["Missing mandatory 'node' argument."]) if !exists($args{node});
	my $t=$args{event};
	if($t !~ /^net\./ && $t !~ /^app\./ && $t !~ /^node\./) {
		return(-1,0,["Type must be 'app.type' or 'node.type' or 'net.type' format."]);
	}
	if($t =~ /^net\./) {
		if(! exists($args{net})) {
			return(-1,0,["Missing mandatory 'net' argument."]);
		}
	}
	if($t =~ /^app\./) {
		if(! exists($args{app})) {
			return(-1,0,["Missing mandatory 'app' argument."]);
		}
	}
	if(! exists($args{app})) {
		$args{app}="";
	}
	if(! exists($args{net})) {
		$args{net}="";
	}
	if(! exists($args{host})) {
		$args{host}=hostname;
	}
	my $event=$t;

	#########################################################
	# Check to see if it appears to be a duplicate, 	#
	# and if so ignore it. A duplicate is considered the	#
	# same event, node and app in the last 5 seconds.	#
	#########################################################

	my $ctime=time;
	if(defined($last_event) && defined($last_time) && $ctime-$last_time<5 && 
		$last_event eq $event && $last_app eq $args{app} &&
		$last_node eq $args{node}) {
		$last_time=$ctime;
		return(0,0,[]);
	}

	#########################################################
	# Record the current event details to ensure no 	#
	# duplicate events are raised.				#
	#########################################################

	$last_time=$ctime;
	$last_event=$event;
	$last_app=$args{app};
	$last_node=$args{node};

	#########################################################
	# Scan the configuration database for any events	#
	# for this type and extract them all.			#
	#########################################################
	my $db=new cldb("$::ENV{TRUECL_CFG}/config.db");
	if(!defined($db)) {
		my ($err_code,$err_string)=cldb::error();
		return(-1,0,["Unable to open configuration database: $err_string [RC=$err_code]."]);
	}
	my $href=$db->get_rows("SELECT * FROM cluster_events WHERE event = '$event';");
	$db->disconnect; $db=undef;
	if(!defined($href) || !defined($href->[0])) {
		return(0,0,[]); # no error, no events processed
	}
	my $count=0;
	my $clreqdxml=undef;
	my $mailx=undef;
	for my $cmail ("/bin/mailx","/usr/bin/mailx","/usr/local/mailx") {
		$mailx=$cmail if -x $cmail;
	}
	my $atext="";
	if(exists($args{atext}) && defined($args{atext})) {
		$atext=" [$args{atext}]";
	}
	my $daemon="N/A";
	if(exists($args{daemon}) && defined($args{daemon})) {
		$daemon=$args{daemon};
	}
	for my $crec (@$href) {
		my $type=$crec->{type};
		if($type eq "log") {
			my $line=sprintf("%d|%s|%s|%s|%s|%s|%s|%s\n",time,hostname,$self->{cluster},$crec->{text} . $atext,$args{daemon},$args{node},$args{net},$args{app});
			my $fname=$crec->{send_file};
			next if !defined($fname);

			if(substr($fname,0,1) eq "/") {
				my $fd;
				open($fd,">>$fname");
				print $fd $line;
				close($fd);
				$count++; next;
			} else {
				my ($rhost,$rfile)=split(/:/,$fname);
				# Send to a remote host here...
				if($rhost eq hostname) {
					my $fd;
					open($fd,">>$rfile");
					print $fd $line;
					close($fd);
					$count++; next;
				} else {
					if(!defined($clreqdxml)) {
						$clreqdxml=new clreqdxml("$::ENV{TRUECL_CFG}/clreqd.xml");
						if(!defined($clreqdxml)) {
							push @warns,"Unable to load Request Daemon configuration info. Reason: " . clreqdxml::error();
							next;
						}
					}
					if($rhost eq '%%MASTER%%') {
						$rhost=get_master_node();
						if(!defined($rhost)) {
							push @warns,"Failed to find master node - cannot append to log file '$rfile'.";
							next;
						}
					}
					my $reqd=new clreq(hosts => [$rhost], clreqdxml => $clreqdxml, logger => $::LOGGER, timeout => 3);
					if(!defined($reqd)) {
						push @warns,"Unable to create a request object - unable to send message to '$rhost'.";
						next;
					}
					my $hh=unpack("h*",$line);
					my $results=$reqd->run(cmd => ["append_to_file","FILE",$rfile,"MSG","HH:$hh"]);
					if(!defined($results->{$rhost})) {
						push @warns,"No results from 'append_to_file' request to '$rhost'.";
						next;
					}
					if($results->{$rhost} =~ /^ERROR\s+/) {
						my $x=$results->{$rhost};
						$x =~ s/^ERROR\s+//;
						push @warns,"Error from 'append_to_file' request on '$rhost': $x";
					}
				}
			}
		}
		if($type eq "cmd") {
			my $line=sprintf("'%d' '%s' '%s' '%s' '%s' '%s' '%s' '%s'",time,hostname,$self->{cluster},$crec->{text} . $atext,$args{daemon},$args{node},$args{net},$args{app});
			my $cmd=$crec->{send_cmd};
			if(!defined($cmd)) {
				push @warns,"No command defined for command type entry for event '$event'.";
				next;
			}
			system("$cmd $line >/dev/null 2>/dev/null </dev/null");
		}
		if($type eq "email") {
			my $recips=$crec->{send_email};
			if(!defined($recips)) {
				push @warns,"Email send event given but no email recipients defined for event '$event'.";
				next;
			}
			if(!defined($mailx)) {
				push @warns,"Email send event given but no 'mailx' reader found for event '$event'.";
				next;
			}
			my $fd;
			open($fd,"|$mailx -s 'Cluster Event for cluster $self->{cluster}' $recips");
			my $line=sprintf("time: %d\nnode: %s\ncluster: %s\ntext:  %s\nevent_daemon: %s\nevent_node: %s\nevent_app: %s\n",time,hostname,$self->{cluster},$crec->{text} . $atext,$args{daemon},$args{node},$args{app});
			print $fd "$line";
			close($fd);
		}
		if($type eq "socket") {
			my $shost=$crec->{send_host};
			my $sport=$crec->{send_port};

			if(!defined($shost)) {
				push @warns,"Socket send event given but no remote host defined for event '$event'.";
				next;
			}
			if(!defined($sport)) {
				push @warns,"Socket send event given but no remote port defined for event '$event'.";
				next;
			}
			if ($sport =~ /\D/) { $sport = getservbyname($sport, 'tcp') }
			if(!defined($sport)) {
				push @warns,"Port name could not be resolved to a port number for event '$event'.";
				next;
			}
			if($shost eq '%%MASTER%%') {
				$shost=get_master_node();
				if(!defined($shost)) {
					push @warns,"Failed to find master node - cannot send to port '$sport'.";
					next;
				}
			}
			my $iaddr=inet_aton($shost);
			if(!defined($iaddr)) {
				push @warns,"Could not convert host name '$shost' to an IP address for event '$event'.";
				next;
			}
			my $sockfd;
			my $paddr=sockaddr_in($sport,$iaddr);
			my $proto=getprotobyname('tcp');
			socket($sockfd,PF_INET, SOCK_STREAM, $proto);
			if(!defined($sockfd)) {
				push @warns,"Unable to open socket for '$shost:$sport' for event '$event'.";
				next;
			}
			if(!connect($sockfd,$paddr)) {
				push @warns,"Unable to connect to remote server '$shost:$sport' for event '$event'.";
				next;
			}

			my $line=sprintf("time: %d\nnode: %s\ncluster: %s\ntext: %s\nevent_daemon: %s\nevent_node: %s\nevent_net: %s\nevent_app: %s\n",time,hostname,$self->{cluster},$crec->{text} . $atext,$args{daemon},$args{node},$args{net},$args{app});
			print $sockfd "$line";
			close($sockfd);
		}
		$count++;
	}
	return(0,$count,[@warns]);
}

}

1;
